package xyz.yb.util;

import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.logging.Logger;
import java.util.logging.Level;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Excel转JSON工具类
 * 将Excel文件转换为JSON格式的工具类。支持合并单元格、多种数据类型和日期格式。
 *
 * 使用说明：
 * 1. Excel文件格式要求：
 *    - 第一行必须是表头
 *    - 支持的单元格类型：文本、数字、日期、布尔值、公式
 *    - 支持合并单元格
 *
 * 2. 输出JSON格式：
 *    - 输出为JSON数组，每行数据对应一个JSON对象
 *    - 使用Excel表头作为JSON对象的键名
 *    - 支持美化输出格式
 *
 * @author YB
 * @version 1.0
 */
public class Excel2Json {
    private static final Logger LOGGER = Logger.getLogger(Excel2Json.class.getName());

    /**
     * 将Excel文件转换为JSON文件
     *
     * @param excelFilePath Excel文件路径
     * @param jsonFilePath 输出JSON文件路径
     * @throws IOException 当文件读写出现错误时抛出
     * @throws IllegalArgumentException 当Excel文件格式不正确时抛出
     */
    public static void convertExcelToJson(String excelFilePath, String jsonFilePath) throws IOException {
        LOGGER.info("开始转换Excel文件: " + excelFilePath);
        ObjectMapper objectMapper = new ObjectMapper();

        try (FileInputStream fis = new FileInputStream(excelFilePath);
             Workbook workbook = new XSSFWorkbook(fis)) {

            Sheet sheet = workbook.getSheetAt(0);

            // 获取表头
            Row headerRow = sheet.getRow(0);
            if (headerRow == null) {
                LOGGER.severe("Excel文件中第1行没有数据，不能获取表头");
                throw new IllegalArgumentException("Excel文件中第1行没有数据，不能获取表头");
            }

            // 存储列名
            LOGGER.fine("开始读取表头信息");
            List<String> headers = new ArrayList<>();
            for (Cell cell : headerRow) {
                String headerName = getCellValueAsString(cell);
                headers.add(headerName);
            }
            LOGGER.info("成功读取表头，共 " + headers.size() + " 列");

            // 创建JSON数组存储所有行数据
            ArrayNode jsonArray = objectMapper.createArrayNode();

            LOGGER.fine("开始处理数据行，共 " + sheet.getLastRowNum() + " 行");
            // 处理数据行
            for (int rowIndex = 1; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
                Row row = sheet.getRow(rowIndex);
                if (row != null) {
                    ObjectNode jsonRow = objectMapper.createObjectNode();

                    // 处理每一列
                    for (int colIndex = 0; colIndex < headers.size(); colIndex++) {
                        // 获取单元格的值（考虑合并单元格的情况）
                        String value = getMergedRegionValue(sheet, rowIndex, colIndex);
                        jsonRow.put(headers.get(colIndex), value);
                    }

                    jsonArray.add(jsonRow);
                }
            }

            LOGGER.info("数据处理完成，开始写入JSON文件: " + jsonFilePath);
            // 将JSON写入文件，使用漂亮的打印格式
            try (FileWriter writer = new FileWriter(jsonFilePath)) {
                objectMapper.writerWithDefaultPrettyPrinter()
                        .writeValue(writer, jsonArray);
                LOGGER.info("JSON文件写入成功");
            }
        } catch (Exception e) {
            LOGGER.log(Level.SEVERE, "转换过程中发生错误", e);
            throw e;
        }
    }

    /**
     * 获取合并单元格的值
     *
     * @param sheet Excel工作表
     * @param rowIndex 行索引
     * @param colIndex 列索引
     * @return 单元格的值，如果单元格为空返回空字符串
     */
    private static String getMergedRegionValue(Sheet sheet, int rowIndex, int colIndex) {
        LOGGER.finest("检查单元格是否在合并区域内: 行=" + rowIndex + ", 列=" + colIndex);
        // 首先检查该单元格是否在合并区域内
        for (int i = 0; i < sheet.getNumMergedRegions(); i++) {
            CellRangeAddress region = sheet.getMergedRegion(i);
            if (region.isInRange(rowIndex, colIndex)) {
                // 如果在合并区域内，返回该区域左上角单元格的值
                Row firstRow = sheet.getRow(region.getFirstRow());
                if (firstRow != null) {
                    Cell firstCell = firstRow.getCell(region.getFirstColumn());
                    return getCellValueAsString(firstCell);
                }
            }
        }

        // 如果不在任何合并区域内，直接返回单元格的值
        Row row = sheet.getRow(rowIndex);
        if (row != null) {
            Cell cell = row.getCell(colIndex);
            return getCellValueAsString(cell);
        }

        return "";
    }

    /**
     * 将单元格的值转换为字符串
     *
     * @param cell Excel单元格
     * @return 单元格的字符串值，如果单元格为空返回空字符串
     */
    private static String getCellValueAsString(Cell cell) {
        if (cell == null) {
            LOGGER.finest("遇到空单元格");
            return "";
        }

        switch (cell.getCellType()) {
            case STRING:
                return cell.getStringCellValue();
            case NUMERIC:
                if (DateUtil.isCellDateFormatted(cell)) {
                    return cell.getLocalDateTimeCellValue().toString();
                }
                return String.valueOf(cell.getNumericCellValue());
            case BOOLEAN:
                return String.valueOf(cell.getBooleanCellValue());
            case FORMULA:
                try {
                    return String.valueOf(cell.getNumericCellValue());
                } catch (Exception e) {
                    LOGGER.warning("公式单元格计算失败，使用字符串值: " + e.getMessage());
                    return cell.getStringCellValue();
                }
            default:
                return "";
        }
    }
}